﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using UILibrary;
using System.Windows.Forms;

namespace OfficeInCloudExcel2003
{
    class ExcelActionPane
    {
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        private Excel.Application m_app;
        private IntPtr m_hExcelWnd = IntPtr.Zero;
        private IntPtr m_hPaneWnd = IntPtr.Zero;
        public ActionPane m_actionPane = new ActionPane();

        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32.dll")]
        static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern System.IntPtr FindWindowEx(System.IntPtr hwndParent, System.IntPtr hwndChildAfter, string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll")]
        static extern bool GetWindowRect(System.IntPtr hWnd, out RECT lpRect);

        delegate int WndProcDelegate(IntPtr hWnd, uint msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, WndProcDelegate proc);

        [DllImport("user32")]
        private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newProc);

        [DllImport("user32.dll")]
        static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [DllImport("user32.dll")]
        static extern int ShowWindow(IntPtr Hwnd, int iCmdShow);

        [DllImport("user32.dll")]
        static extern bool BringWindowToTop(IntPtr hWnd);

        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        private IntPtr m_oldWndProc = IntPtr.Zero;
        private WndProcDelegate m_newProc = null;

        private const UInt32 WM_APP = 0x8000;
        private const UInt32 WM_UPDATE_PARENT = WM_APP + 1;
        private const UInt32 WM_RESIZE_PANE = WM_APP + 2;
        private const UInt32 WM_WINDOWPOSCHANGED = 0x0047;
        private const UInt32 WM_PARENTNOTIFY = 0x0210;
        private const UInt32 WM_DESTROY = 0x0002;
        private const UInt32 WM_CREATE = 0x0001;
        private const UInt32 WM_SIZE = 0x0005;
        private const UInt32 SIZE_MAXIMIZED = 0x0002;
        public const Int32 WM_SYSCOMMAND = 0x112;
        internal const int SW_SHOWMAXIMIZED = 3;
        private const int GWL_WNDPROC = (-4);
        const int SW_MAXIMIZE = 3;
        const int SW_MINIMIZE = 6;
        static readonly IntPtr HWND_TOP = new IntPtr(0);
        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        const UInt32 SWP_SHOWWINDOW = 0x0040;
        const UInt32 SWP_NOZORDER = 0x0004;

        float dpi;

        public ExcelActionPane(Excel.Application app)
        {
            m_app = app;
        }

        private int MineWndProc(IntPtr hWnd, uint msg, int wParam, int lParam)
        {
            switch (msg)
            {
                case WM_UPDATE_PARENT:
                    SetParent(m_actionPane.Handle, m_hPaneWnd);
                    ShowWindow(m_actionPane.Handle, SW_SHOWMAXIMIZED);
                    break;

                //if new window is opened (user creates new workbook or open) then task_pane recreated all childs
                // and we need to set parent again when all previous controls has been created
                case WM_PARENTNOTIFY:
                    if ((UInt32)wParam == WM_CREATE)
                        PostMessage(m_hPaneWnd, (UInt32)WM_UPDATE_PARENT, IntPtr.Zero, IntPtr.Zero);
                    break;
                case WM_RESIZE_PANE:
                    m_actionPane.WindowState = FormWindowState.Normal;
                    RECT rc;
                    GetClientRect(hWnd, out rc);
                    m_actionPane.Height = rc.Bottom - rc.Top;
                    break;
                case WM_WINDOWPOSCHANGED:
                    PostMessage(m_hPaneWnd, (UInt32)WM_RESIZE_PANE, IntPtr.Zero, IntPtr.Zero);
                    break;
            }

            return CallWindowProc(m_oldWndProc, hWnd, msg, wParam, lParam);
        }

        public void resizeActionPane(int width)
        {
            Office.CommandBar cmdPane = null;
            foreach (Office.CommandBar cmd in m_app.CommandBars)
            {
                if (cmd.Name == "Task Pane")
                {
                    cmdPane = cmd;
                    break;
                }
            }

            cmdPane.Position = Office.MsoBarPosition.msoBarRight;
            cmdPane.Visible = true;
            cmdPane.Width = width;
        }

        private void subclassActionPane()
        {
             //refactor this part of finding window, since the potential structure can be different 
            //excel main window
            m_hExcelWnd = FindWindow("XLMAIN", m_app.Caption);
            //excel workbook
            IntPtr hExcelWb = FindWindowEx(m_hExcelWnd, IntPtr.Zero, "EXCEL2", string.Empty);
            m_hPaneWnd = FindWindowEx(hExcelWb, IntPtr.Zero, "MsoCommandBar", "Task Pane");

            // as soon as we set parent we can forget about handle maximize / minimize
            SetParent(m_actionPane.Handle, m_hPaneWnd);

            m_newProc = new WndProcDelegate(MineWndProc);
            m_oldWndProc = SetWindowLong(m_hPaneWnd, GWL_WNDPROC, m_newProc);
        }

        public void buildActionPane()
        {
            subclassActionPane();

            resizeActionPane(313);

            RECT rc;
            GetClientRect(m_hPaneWnd, out rc);
            MatchWordSize(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);

            // we show in maximized state so we can don't worry about fitting
            System.Drawing.Graphics g = m_actionPane.CreateGraphics();
            dpi = g.DpiX;

            m_actionPane.Show();
        }
        
        public void release()
        {
            SetWindowLong(m_hPaneWnd, GWL_WNDPROC, m_oldWndProc);
        }

        private void MatchWordSize(double top, double left, double width, double height)
        {
            m_actionPane.Top = (int)top;
            m_actionPane.Left = (int)left;
            m_actionPane.Height = (int)height;
            m_actionPane.Width = (int)width;
            //m_actionPane.Height = (int)(height * dpi / 72);
            //m_actionPane.Width = (int)(width * dpi / 72);
            //m_actionPane.Top = (int)(top * dpi / 72);
            //m_actionPane.Left = (int)(left * dpi / 72);
        }
    }
}
